#ifndef SCON_RESULT_H_
#define SCON_RESULT_H_

#include "scon_error_handling.h"

#include <windows.h>

#ifdef __SCON_TEST__
#include "gtest/gtest_prod.h"
#endif

#ifdef __SCON_DEBUG__
#include <iostream>
#include <iomanip>
#endif

namespace SCon {

template<class ErrorHandlingPolicy> class FutureResultBase;

typedef FutureResultBase<ConnectionException> FutureResult;

template<class ErrorHandlingPolicy> class FutureResultBase {
/*
класс FutureResultBase предназначен для реализации отложенной доставки результата. (первоночально объект не представляет ценности и достоверный результат может быть получем в любое время вызовом функции GetResult)
Особенность данного класса заключается в том, что копирование объектов этого класса является разрушающим.
Шаблонным параметром класса FutureResultBase является:
- ErrorHandlingPolicy - стратегия обработки ошибок. Класс реализующий данную стратегию должен содержать функцию bool SetErrorState(const std::string&)
*/
#ifdef __SCON_TEST__
	friend class ResultTest;
	FRIEND_TEST(ResultTest, FutureResultWithExceptionTest);
	FRIEND_TEST(ResultTest, FutureResultWithException_CopyConstructorTest);
	FRIEND_TEST(ResultTest, FutureResultWithException_AssignmentOperatorTest);
	FRIEND_TEST(ResultTest, FutureResultWithException_CreateTest);
#endif

public:
/*
Конструктор класса FutureResult принимает следующие параметры:
- event - дескриптор события, активация которого происходит при окончании операции, должен быть корректным.
- dataSize - размер данных подлежащих обработке во время операции, должен быть больше нуля
- actualDataSize - указатель на действительный размер принятых/отправленных данных
- buf - буфер данных, может быть равен 0.
Конструктор в случае ошибок вызывает функцию ErrorHandlingPolicy::SetErrorState(const std::string&);
Следует учитывать также, что в случае если задан указатель на буффер - объект класса не владеет им (буффером),
в случае же если в конструктор в качестве указателя на буффер был передан 0, то объект создаст внутренний буфер, которым он будет владеть.
В последнем случае получить результат можно либо передав указатель на буфер в функцию FutureResultBase<>::GetResult() либо явно вызвав функцию
FutureResultBase<>::GetCopyResult(). В обоих случаях произойдет копирование буфера, а в случае с FutureResultBase<>::GetCopyResult() еще и передача владения буфером.
Если необходимо получить доступ к буфферу объекта класса, не передавая указатель на него во время конструирования, необходимо воспользоваться комбинацией вызовов конструктора по умолчанию и функции FutureResultBase<>::Initialize(), которая возвращает указатель на буффер. Однако следует помнить, что FutureResultBase<>::Initilalize() не передает владения.
*/
	FutureResultBase (HANDLE event, int dataSize, const int* actualDataSize, unsigned char* buf = 0);
	FutureResultBase ();
	~FutureResultBase();

/*
Конструктор копирования класса FutureResultBase работает так, что копируемый объект разрушается. Это необходимо для передачи владения (если он есть) буфером и для избежания возможный ошибок при манипулировании двумя объектами отвечающими за выдачу результата одной и той же операции.
*/
	FutureResultBase(FutureResultBase& otherRes);

/*
Вызов оператора копирования, так же как и вызов копирующего конструктора разрушает копируемый объект. Следует учитывать также, что если объект вызывающий оператор присваивания владел буффером - этот буффер уничтожается и все возможные ссылки и указатели на него становятся невалидными.
*/
	FutureResultBase& operator=(FutureResultBase& otherRes);

/*
Функция
	unsigned char* Initialize(HANDLE event, int dataSize, const int actualDataSize, unsigned char* buf = 0);
инициализирует объект класса FutureResultBase<>
Принимаемые параметры:
- event - дескриптор события, активация которого происходит при окончании операции, должен быть корректным.
- dataSize - размер данных, подлежащих обработке во время операции, должен быть больше 0.
- actualDataSize - указатель на действительный размер принятых/отправленных данных
- buf - буффер данных, может быть равен 0.
Возвращаемое значение:
Указатель на буффер, где будет содержаться достоверный результат по окончании операции. В случае если buf был задан 0 - возвращается указатель на буффер созданный самим оъектом класса FutureResultBase<>, однако владение остается за объектом класса, а не за вызывающим кодом! В случае, если buf  был задан вызывающим кодом, возвращается указатель на него.
Данная Функция в случае ошибок вызывает функцию ErrorHandlingPolicy::SetErrorState( const std::string&)
И, в зависимости от поведения SetErrorState(), либо генерирует исключение, либо возвращает 0.
*/
	unsigned char* Initialize(HANDLE event, int dataSize,  const int* actualDataSize, unsigned char* buf = 0);

/*
Функция
	bool IsDone();
Возвращает true если операция закончена и достоверные данные доступны.
Данная Функция в случае ошибок вызывает функцию ErrorHandlingPolicy::SetErrorState( const std::string&)
И, в зависимости от поведения SetErrorState(), либо генерирует исключение, либо возвращает false.
Следует заметить что ошибки при выполнении этой функции маловероятны и связаны с поведением функции WINAPI WaitForSingleObject()
*/
	bool IsDone() const;

/*
Функция
	int GetResult(unsigned char* buf = 0);
Возвращает достоверный результат операции.
Принимаемые параметры:
- buf - если передан указатель на буфер, то происходит копирование результатa операции в указанный буфер(размер буфeра должен быть подходящим!). Может быть передан 0.
Возвращаемые параметры:
количество считанных/переданных байт.
Данная Функция в случае ошибок вызывает функцию ErrorHandlingPolicy::SetErrorState( const std::string&)
И, в зависимости от поведения SetErrorState(), либо генерирует исключение, либо возвращает -1.
Следует заметить что ошибки при выполнении этой функции маловероятны и связаны с поведением функции WINAPI WaitForSingleObject()
*/
	int GetResult(unsigned char* buf = 0) const;

/*
Функция
	unsigned char* GetCopyResult();
Возвращает указатель на копию буффера, содержащего достоверный результат операции.
Владение буффером на который указывает возвращемый указатель переходи вызывающему коду, который должен позаботится о вызове функции delete[],
когда надобность в буфере отпадет.
*/
	unsigned char* GetCopyResult() const;

private:
	HANDLE operationIsDone_;
	unsigned char* message_;
	int dataSize_;
	const int* actualDataSize_;
	bool isOwnedToBuffer_;

/*
Функция
	void CopyBuf(unsigned char* begin1 , usigned char end1, unsigned char* begin2);
копирует массив bebin1 до элемента end1 включительно в массив begin2
*/
	void CopyBuf(unsigned char* begin1 , unsigned char* end1, unsigned char* begin2) const;
};//end of declaration class FutureResult

template<class ErrorHandlingPolicy>
FutureResultBase<ErrorHandlingPolicy>::FutureResultBase(): operationIsDone_(INVALID_HANDLE_VALUE),
															message_(0),
															dataSize_(0),
															actualDataSize_(0),
															isOwnedToBuffer_(false)
{}

template<class ErrorHandlingPolicy>
FutureResultBase<ErrorHandlingPolicy>::FutureResultBase(HANDLE event, int dataSize, const int* actualDataSize, unsigned char* buf){
	Initialize(event, dataSize, actualDataSize, buf);
}// end of FutureResult<ErrorHandlingPolicy>::FutureResult(HANDLE Event, int dataSize, unsigned char* buf)

template<class ErrorHandlingPolicy>
FutureResultBase<ErrorHandlingPolicy>::~FutureResultBase() {
	if (isOwnedToBuffer_) {
		delete[] message_;
	}/* end of if */
}//end of template<class ErrorHandlingPolicy> FutureResultBase<ErrorHandlingPolicy>::~FutureResultBase()

template<class ErrorHandlingPolicy>
FutureResultBase<ErrorHandlingPolicy>::FutureResultBase(FutureResultBase<ErrorHandlingPolicy>& otherRes): operationIsDone_(otherRes.operationIsDone_),
																						message_(otherRes.message_),
																						dataSize_(otherRes.dataSize_),
																						actualDataSize_(otherRes.actualDataSize_),
																						isOwnedToBuffer_(otherRes.isOwnedToBuffer_)
{
	otherRes.operationIsDone_ = INVALID_HANDLE_VALUE;
	otherRes.message_ = 0;
	otherRes.dataSize_ = 0;
	otherRes.actualDataSize_ = 0;
	otherRes.isOwnedToBuffer_ = false;
}//end of template<class ErrorHandlingPolicy> FutureResultBase<ErrorHandlingPolicy>::FutureResultBase(FutureResultBase&)

template<class ErrorHandlingPolicy>
FutureResultBase<ErrorHandlingPolicy>& FutureResultBase<ErrorHandlingPolicy>::operator=(FutureResultBase<ErrorHandlingPolicy>& otherRes) {
#ifdef __SCON_DEBUG__
#define _D_Assignment_ 1
#else
#define _D_Assignment_ 0
#endif

//уничтожаем буффер, которым владели
	if (isOwnedToBuffer_) {
		delete[] message_;

		if (_D_Assignment_) {
			std::cout << "buffer destroied\n";
		}/* end of if */
	}/* end of if */

//подменяем данные
	operationIsDone_ = otherRes.operationIsDone_;
	message_ = otherRes.message_;
	dataSize_ = otherRes.dataSize_;
	actualDataSize_ = otherRes.actualDataSize_;
	isOwnedToBuffer_ = otherRes.isOwnedToBuffer_;

//очищаем копируемый объект
	otherRes.operationIsDone_ = INVALID_HANDLE_VALUE;
	otherRes.message_ = 0;
	otherRes.dataSize_ = 0;
	otherRes.actualDataSize_ = 0;
	otherRes.isOwnedToBuffer_ = false;

	return *this;
}//end of template<ErrorHandlingPolicy> FutureResultBase<ErrorHandlingPolicy>& FutureResultBase<ErrorHandlingPolicy>::operator= ()

template<class ErrorHandlingPolicy>
unsigned char* FutureResultBase<ErrorHandlingPolicy>::Initialize(HANDLE event,
																int dataSize,
																const int* actualDataSize,
																unsigned char* buf) {
	if (event == INVALID_HANDLE_VALUE) {
		ErrorHandlingPolicy::SetErrorState("Bad parameter 'event'");
		return 0;
	}/* end of if */

	if (dataSize <= 0) {
		ErrorHandlingPolicy::SetErrorState("Bad parameter 'dataSize'");
		return 0;
	}/* end of if */

	if (actualDataSize == 0) {
		ErrorHandlingPolicy::SetErrorState("Bad parameter 'actualDataSize'");
	}/* end of if */

	operationIsDone_ = event;
	dataSize_ = dataSize;
	actualDataSize_ = actualDataSize;

	if (buf == 0) {
		message_ = new unsigned char[dataSize_];
		isOwnedToBuffer_ = true;
		return message_;
	}/* end of if */

	message_ = buf;
	isOwnedToBuffer_ = false;
	return message_;
}//end of template<class ErrorHandlingPolicy> unsigned char* FutureResultBase<ErrorHandlingPolicy>::Initialize()

template<class ErrorHandlingPolicy>
bool FutureResultBase<ErrorHandlingPolicy>::IsDone() const {
	DWORD res = WaitForSingleObject(operationIsDone_, 0);
	
	if (res == WAIT_FAILED) {
		return ErrorHandlingPolicy::SetErrorState("Call of function WINAPi WaitForSingleObject() is not right?");
	}/* end of if */

	if (res == WAIT_TIMEOUT) {
		return false;
	}/* end of if */

	return true;
}//end of template<class ErrorHandlingPolicy> bool FutureResultBase<ErrorHandlingPolicy>::IsDone ()

template<class ErrorHandlingPolicy>
int FutureResultBase<ErrorHandlingPolicy>::GetResult(unsigned char* buf) const {
	DWORD res = WaitForSingleObject(operationIsDone_, INFINITE);

	if (res == WAIT_FAILED) {
		ErrorHandlingPolicy::SetErrorState("Call of function WINAPi WaitForSingleObject() is not right?");
		return -1;
	}/* end of if */
	if (buf != 0) {
		CopyBuf(message_, message_ + *actualDataSize_, buf); 
	}/* end of if */

	return *actualDataSize_;
}//end of template<class ErrorHandlingPolicy> int FutureResultBase<ErrorHandlingPolicy>::GetResult()

template<class ErrorHandlingPolicy> unsigned char* FutureResultBase<ErrorHandlingPolicy>::GetCopyResult() const {
	unsigned char* copyBuf = new unsigned char[*actualDataSize_];
	CopyBuf(message_, message_ + *actualDataSize_, copyBuf);

	return copyBuf;
}//end of template<class ErrorHandlingPolicy> unsigned char* FutureResultBase<ErrorHandlingPolicy>::GetCopyResult()

template<class ErrorHandlingPolicy>
void FutureResultBase<ErrorHandlingPolicy>::CopyBuf(unsigned char* begin1, unsigned char* end1, unsigned char* begin2) const {
	unsigned char* it1 = begin1;
	unsigned char* it2 = begin2;
	while(it1 != end1) {
		*it2 = *it1;
		++it1;
		++it2;
	}//end of while
}//end of template<class ErrorHandlingPolicy> void FutureResultBase<ErrorHandlingPolicy>::CopyBuf()

} /* SCon */ 

#endif /* end of include guard: SCON_RESULT_H_ */

